/************************************************************************
* Programmer:  Philippe Gambling
*
* Course:  CSCI 4534
*
* Date:  April 6, 2009
*
* Assignment:  #3
*
* Environment:  GCC 3.2.2 running on diamond.rocks.uhcl.edu
*
* Files Included: client.c, PCB.h, client
*
* Purpose:  To ask user for a sequence of alternating cpu and I/O bursts
*           and arrival time value, 
*           send a PCB with these values to the server,
*           and receive the PCB back from the server with updated 
*           values for completion, blocked, waiting, and turnaround times.
*
* Input:  An array of integer values for the cpu and I/O burst sequence
*         and an arrival time all from the command-line.
*
* Preconditions:  The server program is already running and FIFO_TO_SERVER exists.
*
* Output:  The completion, blocked (doing I/O), blocked (waiting for I/O),
*          waiting (in ready state), and turnaround times returned from the server for this process.
*
* Postconditions:  After outputting the results, the client program deletes FIFO_PID and exits.        
*
* Algorithm:
*          Get the process ID (PID).
*          Create a private FIFO named "FIFO_PID", where PID is the process id number.
*          Open FIFO_TO_SERVER as write only.
*          Read CPU and I/O burst sequence from command-line.
*          Read an integer value for the arrival time from the command-line.
*          Create a new PCB stuct.
*          Assign the PCB's Pid to PID, ClientFifoName to "FIFO_PID", 
*            and BurstValues and ArrivalTime the user inputs.
*          Write the PCB to FIFO_TO_SERVER.
*          Open the FIFO_PID as read only.
*          Read a PCB from FIFO_PID.
*          Print contents of PCB from server to screen.
*          Close FIF0_TO_SERVER and FIFO_PID.
*          Unlink FIFO_PID.
***********************************************************************/ 

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/errno.h>
#include <fcntl.h>

extern int errno;

/* Custom includes created for this assignment */
#include "pcb.h"  // Defines a struct to model a process control blockn

/* Function prototypes */
void CreatePrivateFifo(char *);
void AssignBurstValues(char str[],int array[]);

int main(int argc, char * argv[])
{
  int fda;			// to write to server
  int fdb;			// to read response from server
  char testOutputFile[25]; // Name of file to write test results to  
  int testmode = 0; // Output extra information for testing
  int i = 0; // Loop counter
  
  PCB toServer = NEW_PCB;   // Message to send server
  PCB fromServer = NEW_PCB; // Message received from server

  // Get CPU burst and arrival times from user
  if(argc < 3) {
    printf("Usage: ./client \"BURST_VALUES\" ARRIVAL_TIME\n");
    printf("      BURST_VALUES - Alternating sequence of CPU and I/O burst times, contained within \"\" (Odd number of bursts, positive whole numbers only)\n");    
    printf("      ARRIVAL_TIME - Simulated arrival time for this client. (positive whole number only)\n");    
    exit(-1);   
  } 
  else { // Collect command-line arguments (used for testing)
    AssignBurstValues(argv[1],toServer.BurstValues);
    printf("Client: argv[1] = %s\n",argv[1]);
    toServer.ArrivalTime = atoi(argv[2]);    
    if(argc > 3) {
      strcpy(testOutputFile,argv[3]);
      testmode = 1;
    }
  }

  // Assign the current process id to the PCB
  toServer.Pid = (int)getpid();
  
  // Create a private FIFO to server to client communcation
  CreatePrivateFifo(toServer.ClientFifoName);

  // Open FIFO_TO_SERVER to send data to server 
  printf("Client: Opening FIFO_TO_SERVER to write\n");
  if((fda=open("FIFO_TO_SERVER", O_WRONLY))<0)
     printf("cant open FIFO_TO_SERVER to write\n");
  
  
  printf("Client: Sending the following message to server: \n");
  PCB_Print(&toServer);
  write(fda, &toServer, sizeof(PCB)); // Send data to server
  printf("Client: Sent data to server\n");
  
  // Open ClientFifoName to receive data from server
  printf("Client: Opening %s to read server response\n", toServer.ClientFifoName);
  if((fdb=open(toServer.ClientFifoName, O_RDONLY))<0)
	  printf("cant open %s to read", toServer.ClientFifoName);
  
  printf("Client: Waiting for response...\n");
  while(read(fdb, &fromServer, sizeof(PCB))<=0); // Get average from server
  
  printf("Client: Got the response from server\n");
  printf("Client: Received the following message from server: \n");
  PCB_Print(&fromServer);  
 
  // Close FIFOs
  close(fda);
  close(fdb);
  
  // Clean up private fifo
  unlink(toServer.ClientFifoName);

  // Print important data to a file
  if(testmode) { 
    FILE *file;
    file = fopen(testOutputFile,"w");
    fprintf(file,"BurstValues sent = [ "); 
    for(i=0;toServer.BurstValues[i]!=0;i++) { fprintf(file,"%d ",toServer.BurstValues[i]); }  
    fprintf(file,"]\n");     
    fprintf(file,"BurstIndex sent = %d\n",toServer.BurstIndex);        
    fprintf(file,"Arrival time sent = %d\n",toServer.ArrivalTime);
    fprintf(file,"Completion time sent = %d\n",toServer.CompletionTime);
    fprintf(file,"BlockedIO time sent = %d\n",toServer.BlockedIO);
    fprintf(file,"BlockedWait time sent = %d\n",toServer.BlockedWait);                    
    fprintf(file,"Wait time sent = %d\n",toServer.WaitTime);
    fprintf(file,"Turnaround time sent = %d\n",toServer.TurnaroundTime);
    fprintf(file,"BurstValues received = [ "); 
    for(i=0;fromServer.BurstValues[i]!=0;i++) { fprintf(file,"%d ",fromServer.BurstValues[i]); }  
    fprintf(file,"]\n");         
    fprintf(file,"BurstIndex received = %d\n",fromServer.BurstIndex);    
    fprintf(file,"Arrival time received = %d\n",fromServer.ArrivalTime);
    fprintf(file,"Completion time received = %d\n",fromServer.CompletionTime);
    fprintf(file,"BlockedIO time received = %d\n",fromServer.BlockedIO);
    fprintf(file,"BlockedWait time received = %d\n",fromServer.BlockedWait);                    
    fprintf(file,"Wait time received = %d\n",fromServer.WaitTime);
    fprintf(file,"Turnaround time received = %d\n",fromServer.TurnaroundTime);    
    fclose(file);
  }

  printf ("Client: All done!\n");
 
  return 0;
}
void CreatePrivateFifo(char * fifoName)
/************************************************************************
* Purpose:  To create a FIFO name "FIFO_PID", where PID is the current
*           process's ID.
*
* Parameters:  A string to return the new FIFO's name.
*
* Action:  The fifo is written to fifoname.
*
***********************************************************************/ 
{ 
  sprintf(fifoName, "FIFO_%d", (int)getpid());
  
  printf("Client: FIFO name is %s \n", fifoName);
  if ((mkfifo(fifoName,0666)<0 && errno != EEXIST))
  {
	  perror("cant create private fifo");
	  exit(-1);
  }
  printf("Client: Succesfully created %s \n", fifoName); 
}

void AssignBurstValues(char str[],int array[])
{
/************************************************************************
* Purpose:  To assign the CPU and I/O burst values to an integer array
*           from a string of numbers.
*
* Parameters:  A string with numbers
*
* Action:  array is assigned the numbers passed in str, if the array
*          is an invalid length then the program exits with an error.
*
***********************************************************************/
  int i = 0;
  char *val = strtok(str," ");
  char msg[51];
  
  for(i=0;i<MAX_BURSTS && val!=NULL;i++) {
    array[i] = atoi(val);
    val = strtok(NULL," ");
  }
  if(val != NULL) { // Make sure all burst values were captured
    sprintf(msg,"Error: The number of burst values exceeded %d\n",MAX_BURSTS);
    perror(msg);
    exit(-1);
  } 
  else if((i)%2 == 0) { // Make sure array length is odd
    perror("Error: The number of burst values must be odd.\n");
    exit(-1);
  }
}
